package com.black.common.feign;

import com.black.common.constant.ShareConstant;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 外部API抽象类型
 * 可通过重写getHttpHeader方法去实现
 * 添加Token/Cookie/ContentType等信息
 *
 * @author linwenfeng
 */
public abstract class AbstractRestApi implements RestApi {

    @Resource
    private RestTemplate restTemplate;

    @Override
    public LinkedMultiValueMap<String, String> getHttpBasicHeader() {
        LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>();
        header.add("accept", "*/*");
        header.add("Content-Type", "application/json");
        return header;
    }

    @Override
    public Map<String, String> getHttpExtraHeader(Map<String, Object> params) {
        return null;
    }

    protected <T> ResponseEntity<T> execute(Map<String, Object> params, Class<T> target) {
        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(getHttpRequestBody(params), getHttpHeader(params));
        return restTemplate.exchange(getExecuteUrl(params), getHttpMethod(), httpEntity, target);
    }


    protected <T> ResponseEntity<T> execute(Object object, Class<T> target) {
        return execute(convertToMap(object), target);
    }

    protected <T> ResponseEntity<T> execute(Class<T> target) {
        return execute(convertToMap(null), target);
    }

    protected <T> ResponseEntity<T> execute(Class<T> target, Object... params) {
        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(getHttpRequestBody(null), getHttpHeader(null));
        return restTemplate.exchange(getExecuteUrl(params), getHttpMethod(), httpEntity, target);
    }

    private Map<String, Object> getHttpRequestBody(Map<String, Object> params) {
        if (HttpMethod.POST.equals(getHttpMethod())) {
            return params;
        }
        return null;
    }

    private LinkedMultiValueMap<String, String> getHttpHeader(Map<String, Object> params) {
        LinkedMultiValueMap<String, String> httpBasicHeader = getHttpBasicHeader();
        Map<String, String> httpExtraHeader = getHttpExtraHeader(params);
        if (httpExtraHeader != null) {
            httpExtraHeader.forEach(httpBasicHeader::add);
        }
        return httpBasicHeader;
    }

    private String getExecuteUrl(Map<String, Object> params) {
        if (HttpMethod.POST.equals(getHttpMethod()) || params == null) {
            return getUrl();
        }
        StringBuilder urlBuilder = new StringBuilder(getUrl());
        params.forEach((key, value) -> {
            if (urlBuilder.indexOf(ShareConstant.Symbol.QUESTION) > 0) {
                urlBuilder.append(ShareConstant.Symbol.AND).append(key).append(ShareConstant.Symbol.EQUALS).append(value);
            } else {
                urlBuilder.append(ShareConstant.Symbol.QUESTION).append(key).append(ShareConstant.Symbol.EQUALS).append(value);
            }
        });
        return urlBuilder.toString();
    }

    private String getExecuteUrl(Object... params) {
        return String.format(getUrl(), params);
    }

    private Map<String, Object> convertToMap(Object obj) {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>(8);
        //获取类的各个属性值
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            //获取类的属性名称
            String fieldName = field.getName();
            //获取类的属性名称对应的值
            if (getValueByFieldName(fieldName, obj) != null) {
                map.put(fieldName, getValueByFieldName(fieldName, obj));
            }
        }
        return map;
    }

    /**
     * 根据属性名获取该类此属性的值
     *
     * @param fieldName f
     * @param object    o
     * @return r
     */
    private Object getValueByFieldName(String fieldName, Object object) {
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String getter = "get" + firstLetter + fieldName.substring(1);
        try {
            Method method = object.getClass().getMethod(getter);
            return method.invoke(object);
        } catch (Exception e) {
            return null;
        }
    }
}
